#include "CondVar.hh"
#include "AutoLock.hh"

CondVar::CondVar()
{
	State = false;
	
	WaitingTime = -1;
	NumOfWaiting = 0;
	BlockingWaiting = false;
	
	ResultOf Result;
	Result = initObj();
	checkErrorFV(Result, "Fail init.");
	
	State = true;
}

CondVar::~CondVar()
{
	ResultOf Result;
	Result = release();
	checkErrorWOR(Result, "Fail release.");
	
	Result = destroyObj();
	checkErrorWOR(Result, "Fail destroyObj.");
}

ResultOf CondVar::post()
{
	if (!State)
	{
		fprintf(stderr, "This instance(%x) is not available.\n", (Number)this);
		return RETURN_ERROR;
	}
	
	ResultOf Result;
	AutoLock AutoLockOfWaiting(&LockOfWaiting);
	
	if (--NumOfWaiting >= 0)
	{
		Result = signal();
		checkErrorWR(Result, "Fail signal.");
	}
	
	return RETURN_OK;
}

ResultOf CondVar::pend()
{
	if (!State)
	{
		fprintf(stderr, "This instance(%x) is not available.\n", (Number)this);
		return RETURN_ERROR;
	}
	
	ResultOf Result;
	AutoLock AutoLockOfWaiting(&LockOfWaiting);
	
	if (++NumOfWaiting <= 0)
		return RETURN_OK;
	
	if (BlockingWaiting)
	{
		--NumOfWaiting;
		return RETURN_BLOCK;
	}
	
	Result = waitSignal();
	if (checkError(Result))
	{
		if (Result == RETURN_TIMEOUT)
			--NumOfWaiting;
		else
			notifyError(Result, "Fail waitSignal.");
		
		return Result;
	}
	
	return RETURN_OK;
}

ResultOf CondVar::setTimeout(Number MicroSec)
{
	WaitingTime = MicroSec;
	return RETURN_OK;
}

ResultOf CondVar::release()
{
	if (!State)
	{
		fprintf(stderr, "This instance(%x) is not available.\n", (Number)this);
		return RETURN_ERROR;
	}
	
	if (NumOfWaiting <= 0)
		return RETURN_OK;
	
	ResultOf Result;
	AutoLock AutoLockOfWaiting(&LockOfWaiting);
	
	NumOfWaiting = 0;
	
	Result = broadcast();
	checkErrorWR(Result, "Fail broadcast.");
	
	return RETURN_OK;
}

ResultOf CondVar::preventWaiting()
{
	BlockingWaiting = true;
	return RETURN_OK;
}

ResultOf CondVar::allowWaiting()
{
	BlockingWaiting = false;
	return RETURN_OK;
}

ResultOf CondVar::init()
{
	AutoLock AutoKey(&LockOfWaiting);
	
	NumOfWaiting = 0;
	return RETURN_OK;
}

Number CondVar::tellWaiting()
{
	return NumOfWaiting;
}

ResultOf CondVar::initObj()
{
	ResultOf Result;
	Result = pthread_cond_init(&ObjOfCondVar, NULL);
	checkErrorWR(Result, "Fail pthread_cond_init.");
	
	return RETURN_OK;
}

ResultOf CondVar::destroyObj()
{
	ResultOf Result;
	Result = pthread_cond_destroy(&ObjOfCondVar);
	checkErrorWR(Result, "Fail pthread_cond_destroy.");
	
	return RETURN_OK;
}

ResultOf CondVar::signal()
{
	ResultOf Result;
	Result = pthread_cond_signal(&ObjOfCondVar);
	checkErrorWR(Result, "Fail pthread_cond_signal.");
	
	return RETURN_OK;
}

ResultOf CondVar::waitSignal()
{
	if (WaitingTime == 0)
		return RETURN_OK;
	
	timeval Now;
	ResultOf Result;
	timespec SpecWaitingTime;
	
	if (WaitingTime != -1)
	{
		Result = gettimeofday(&Now, NULL);
		checkErrorWR(Result, "Fail gettimeofday.");
		
		Result = makeSpecWaitingTime(&Now, &SpecWaitingTime);
		checkErrorWR(Result, "Fail convert.");
		
		Result = pthread_cond_timedwait(&ObjOfCondVar, getMutexObj(), &SpecWaitingTime);
		if (checkError(Result))
		{
			if (Result != RETURN_TIMEOUT)
				notifyError(Result, "Fail pthread_cond_timedwait.");
			
			return Result;
		}
	}
	else
	{
		Result = pthread_cond_wait(&ObjOfCondVar, getMutexObj());
		checkErrorWR(Result, "Fail pthread_cond_wait.");
	}
	
	return RETURN_OK;
}

ResultOf CondVar::broadcast()
{
	ResultOf Result;
	Result = pthread_cond_broadcast(&ObjOfCondVar);
	checkErrorWR(Result, "Fail pthread_cond_broadcast.");
	
	return RETURN_OK;
}

pthread_mutex_t* CondVar::getMutexObj()
{
	 return LockOfWaiting.tellObj();
}

ResultOf CondVar::makeSpecWaitingTime(timeval* TimeVal, timespec* TimeSpec)
{
	TimeSpec->tv_sec = TimeVal->tv_sec;
	TimeSpec->tv_nsec = (TimeVal->tv_usec + WaitingTime) * 1000;
	
	if (TimeSpec->tv_nsec >= MaxNanoSec)
	{
		TimeSpec->tv_sec += TimeSpec->tv_nsec / MaxNanoSec;
		TimeSpec->tv_nsec %= MaxNanoSec;
	}
	
	return RETURN_OK;
}
